﻿using SoftwareConsulting.BI11.NumericalMethods.Interfaces;
using SoftwareConsulting.BI11.NumericalMethods.MatrixAlgebra;

namespace SoftwareConsulting.BI11.NumericalMethods.Optimization
{
    /// <summary>
    /// Класс проекции многоаргументной функции на направление одного из измерений
    /// </summary>
    public class VectorProjectedFunction : IOneVariableFunction
    {	    
        private IManyVariableFunction _function;//оптимизируемая функция	    
        private Vector _origin;//начальная точка вычисления функции
        private Vector _direction;//направление вычисления функции

        /// <summary>
        /// Конструктор класса
        /// </summary>
        /// <param name="function">Функция, требующая решения</param>
        /// <param name="origin">Начальная точка вычисления функции</param>
        /// <param name="direction">Направление вычисления функции</param>
        public VectorProjectedFunction(IManyVariableFunction function, double[] origin, double[] direction)
        {
            _function = function;
            SetOrigin(origin);
            SetDirection(direction);
        }

        /// <summary>
        /// Конструктор класса
        /// </summary>
        /// <param name="function">Функция, требующая решения</param>
        /// <param name="origin">Начальная точка вычисления функции</param>
        /// <param name="direction">Направление вычисления функции</param>
        public VectorProjectedFunction(IManyVariableFunction function, Vector origin, Vector direction)
        {
            _function = function;
            this.Origin = origin;
            this.Direction = direction;
        }
        
        /// <summary>
        /// Возвращает начальную точку вычисления функции, сдвинутую указанным образом
        /// </summary>
        /// <param name="shift">Начальная точка вычисления функции</param>
        /// <returns></returns>
        public Vector GetOriginShiftedBy(double shift)
        {
            Vector v = _direction * shift;
            v.Add(_origin);
            return v;
        }

        /// <summary>
        /// Возвращает/устанавливает направление вычисления функции
        /// </summary>
        public Vector Direction
        {
            get 
            { 
                return _direction; 
            }
            set 
            { 
                _direction = value; 
            }
        }

        /// <summary>
        /// Возвращает/устанавливает начальную точку вычисления функции
        /// </summary>
        public Vector Origin
        {
            get 
            { 
                return _origin; 
            }
            set 
            {
                _origin = value; 
            }
        }

        /// <summary>
        /// Устанавливает направление вычисления функции
        /// </summary>
        /// <param name="newDirection"></param>
        public void SetDirection(double[] newDirection)
        {
            _direction = new Vector(newDirection);
        }

        /// <summary>
        /// Устанавливает начальную точку вычисления функции
        /// </summary>
        /// <param name="newOrigin"></param>
        public void SetOrigin(double[] newOrigin)
        {
            _origin = new Vector(newOrigin);
        }

        /// <summary>
        /// Возвращает как строку
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return string.Format("{0} -> {1}", _origin, _direction);
        }
        
        /// <summary>
        /// Возвращает значение функции, на указанном расстоянии от начала
        /// </summary>
        /// <param name="distanceFromOrigin"></param>
        /// <returns></returns>
        public double GetValue(double distanceFromOrigin)
        {
            try
            {
                return _function.GetValue(GetOriginShiftedBy(distanceFromOrigin).CloneComponents());
            }
            catch (BIMatrixIllegalDimensionException) 
            { 
                return double.NaN; 
            };
        }
    }
}
